package pers.xiaojun.boot.module.system.service.user;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Sets;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validator;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import pers.xiaojun.boot.common.enums.CommonStatusEnum;
import pers.xiaojun.boot.common.exception.BusinessException;
import pers.xiaojun.boot.common.util.CollectionUtils;
import pers.xiaojun.boot.module.infra.api.config.ConfigApi;
import pers.xiaojun.boot.module.system.constants.RedisKeyConstants;
import pers.xiaojun.boot.module.system.controller.user.vo.user.*;
import pers.xiaojun.boot.module.system.constants.ConfigKeyConstants;
import pers.xiaojun.boot.module.system.dao.entity.dept.UserPostDO;
import pers.xiaojun.boot.module.system.dao.mapper.user.AdminUserMapper;
import pers.xiaojun.boot.module.system.dao.entity.user.AdminUserDO;
import pers.xiaojun.boot.module.system.service.dept.DeptService;
import pers.xiaojun.boot.module.system.service.dept.PostService;
import pers.xiaojun.boot.module.system.service.dept.UserPostService;

import java.util.*;

import static pers.xiaojun.boot.module.system.constants.BusinessCodeConstants.*;

/**
 * 用户服务层实现类
 *
 * @author xiaojun
 * @since 2025-10-03
 */
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<AdminUserMapper, AdminUserDO> implements UserService {

    private final DeptService deptService;
    private final PostService postService;
    private final UserPostService userPostService;
    private final PasswordEncoder passwordEncoder;
    private final ConfigApi configApi;
    private final Validator validator;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AdminUserDO saveUser(UserSaveReqVO saveVO) {
        // 校验基础信息
        validateUserBasicInfo(saveVO.getUsername(), saveVO.getEmail(), saveVO.getMobile(), null);

        // 校验部门
        if (saveVO.getDeptId() != null) {
            deptService.validateDeptIsEnabled(saveVO.getDeptId());
        }
        // 校验岗位
        if (CollUtil.isNotEmpty(saveVO.getPostIds())) {
            validatePosts(saveVO.getPostIds());
        }

        // 写入用户
        AdminUserDO user = BeanUtil.toBean(saveVO, AdminUserDO.class);
        user.setPassword(passwordEncoder.encode(saveVO.getPassword()));
        user.setStatus(CommonStatusEnum.ENABLE.getStatus());
        this.baseMapper.insert(user);

        // 写入用户岗位
        userPostService.saveBatch(CollectionUtils.convertSet(
                saveVO.getPostIds(),
                postId -> new UserPostDO().setPostId(postId).setUserId(user.getId()))
        );

        return user;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AdminUserDO updateUser(UserSaveReqVO updateVO) {
        // 密码和用户名无法更新，进行置空处理
        updateVO.setUsername(null);
        updateVO.setPassword(null);

        // 校验基础信息
        validateUserBasicInfo(null, updateVO.getEmail(), updateVO.getMobile(), updateVO.getId());

        // 校验部门
        if (updateVO.getDeptId() != null) {
            deptService.validateDeptIsEnabled(updateVO.getDeptId());
        }

        AdminUserDO user = BeanUtil.toBean(updateVO, AdminUserDO.class);
        this.baseMapper.updateById(user);

        // 原来的拥有的岗位Id
        Set<Long> oldPostIds = Sets.newHashSet(userPostService.listPostIdsByUserId(user.getId()));
        // 现在的岗位Id
        Set<Long> newPostIds = updateVO.getPostIds();
        // oldPostIds - newPostIds = 需要删除的岗位Id
        userPostService.removeUserPostByUserIdAndPostIds(user.getId(), Sets.difference(oldPostIds, newPostIds));

        // 写入用户岗位，newPostIds - oldPostIds = 需要新增的岗位Id
        userPostService.saveBatch(CollectionUtils.convertSet(
                Sets.difference(newPostIds, oldPostIds),
                postId -> new UserPostDO().setPostId(postId).setUserId(user.getId()))
        );

        return user;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserImportRespVO importUsers(List<UserImportExcelVO> userImports, Boolean isOverwrite) {
        LinkedList<String> created = new LinkedList<>();
        LinkedList<String> updated = new LinkedList<>();
        Map<String, String> failed = new HashMap<>();

        for (UserImportExcelVO userImport : userImports) {
            // 校验器校验信息
            Set<ConstraintViolation<UserSaveReqVO>> violations = validator.validate(BeanUtil.toBean(userImport, UserSaveReqVO.class));
            if (!violations.isEmpty()) {
                failed.put(userImport.getUsername(), violations.iterator().next().getMessage());
                continue;
            }

            AdminUserDO existingUser = this.baseMapper.selectByUsername(userImport.getUsername());
            // 如果用户存在，且不为覆盖模式，则记录错误
            if (existingUser != null && !isOverwrite) {
                failed.put(userImport.getUsername(), USER_USERNAME_EXISTS.getMessage());
                continue;
            }
            // 校验基础信息，将错误搜集在 failed 中
            Long userId = existingUser == null ? null : existingUser.getId();
            try {
                // 这里不用再校验用户名，在上面已经校验了
                validateUserBasicInfo(null, userImport.getEmail(), userImport.getMobile(), userId);
                // 校验部门
                if (userImport.getDeptId() != null) {
                    deptService.validateDeptIsEnabled(userImport.getDeptId());
                }
            } catch (BusinessException ex) {
                failed.put(userImport.getUsername(), ex.getMessage());
                continue;
            }

            AdminUserDO targetUser = BeanUtil.toBean(userImport, AdminUserDO.class);

            // existingUser 现有用户如果为null，则为写入
            if (existingUser == null) {
                targetUser.setPassword(passwordEncoder.encode(configApi.getConfigValueByKey(ConfigKeyConstants.SYSTEM_USER_INIT_PASSWORD)));
                this.baseMapper.insert(targetUser);
                created.add(userImport.getUsername());
            } else {
                targetUser.setId(userId);
                this.baseMapper.updateById(targetUser);
                updated.add(userImport.getUsername());
            }
        }

        return new UserImportRespVO()
                .setCreateUsernames(created)
                .setUpdateUsernames(updated)
                .setFailureUsernames(failed);
    }

    @Override
    public void updateUserStatus(UserStatusUpdateReqVO updateStatusVO) {
        validateUserExists(updateStatusVO.getId());
        AdminUserDO user = new AdminUserDO();
        user.setId(updateStatusVO.getId());
        user.setStatus(updateStatusVO.getStatus());
        this.baseMapper.updateById(user);
    }

    @Override
    public void updateUserPassword(UserStatusPasswordReqVO updatePasswordVO) {
        validateUserExists(updatePasswordVO.getId());
        AdminUserDO user = new AdminUserDO();
        user.setId(updatePasswordVO.getId());
        user.setPassword(passwordEncoder.encode(updatePasswordVO.getPassword()));
        this.baseMapper.updateById(user);
    }


    @Override
    @CacheEvict(value = RedisKeyConstants.Cache.USER_ROLE_IDS, key = "#userId")
    public void deleteUserById(Long userId) {
        validateUserExists(userId);

        this.baseMapper.deleteById(userId);
    }

    @Override
    @CacheEvict(value = RedisKeyConstants.Cache.USER_ROLE_IDS, key = "#userIds")
    public void deleteUserByIds(List<Long> userIds) {
        List<AdminUserDO> userList = this.baseMapper.selectByIds(userIds);

        if (userList.size() != userIds.size()) {
            Set<Long> notFindIds = Sets.difference(
                    new HashSet<>(userIds),
                    CollectionUtils.convertSet(userList, AdminUserDO::getId)
            );
            throw new BusinessException(USER_NOT_EXISTS.getCode(), StrUtil.format("用户Id：{} 不存在", notFindIds));
        }

        this.baseMapper.deleteByIds(userIds);
    }

    @Override
    public AdminUserDO getUserById(Long userId) {
        return validateUserExists(userId);
    }

    @Override
    public AdminUserDO getUserByUsername(String username) {
        return this.baseMapper.selectByUsername(username);
    }

    @Override
    public Page<AdminUserDO> listUsersPage(UserPageReqVO pageVO) {
        return this.baseMapper.selectByPage(pageVO);
    }

    @Override
    public AdminUserDO validateUserExists(Long userId) {
        AdminUserDO user = this.baseMapper.selectById(userId);
        if (user == null) {
            throw new BusinessException(USER_NOT_EXISTS);
        }
        return user;
    }

    /**
     * 校验用户基础信息（为空跳过校验）
     *
     * @param username 账户名
     * @param email    邮箱
     * @param mobile   手机号
     * @param userId   用户id
     */
    private void validateUserBasicInfo(String username, String email, String mobile, Long userId) {
        AdminUserDO user;
        if (StrUtil.isNotEmpty(username)) {
            user = this.baseMapper.selectByUsername(username);
            if (user != null && !user.getId().equals(userId)) {
                throw new BusinessException(USER_USERNAME_EXISTS);
            }
        }
        if (StrUtil.isNotEmpty(email)) {
            user = this.baseMapper.selectByEmail(email);
            if (user != null && !user.getId().equals(userId)) {
                throw new BusinessException(USER_EMAIL_EXISTS);
            }
        }
        if (StrUtil.isNotEmpty(mobile)) {
            user = this.baseMapper.selectByMobile(mobile);
            if (user != null && !user.getId().equals(userId)) {
                throw new BusinessException(USER_MOBILE_EXISTS);
            }
        }

    }


    /**
     * 校验岗位
     *
     * @param postIds 岗位Id集合
     */
    private void validatePosts(Set<Long> postIds) {
        for (Long postId : postIds) {
            postService.validatePostExists(postId);
        }
    }
}
